/**
 *
 * teetang; ningxiaotao
 * 79503983@qq.com; ningxiaotao@126.com
 * 2009-03-12
 *
 */
function FormValidation(form, stopAtInvalid, onSuccess) {
	if(!form) return;
	this.form = form.nodeName ? form : document.getElementById(form);
	if(!this.form) return;
	
	this.stopAtInvalid = stopAtInvalid || false;
	this.onSuccess = onSuccess || function() {}
	
	var old_onsubmit = this.form.onsubmit || function() {};
	var self = this;
	this.form.onsubmit = function(e) { return self.validate() && (old_onsubmit.call(this, e) !== false) && (self.onSuccess.call(this, e) !== false); }
	this.elementValidations = new Array();
}

FormValidation.prototype.validate = function() {
	var isValid = true;
	for(var i=0; i<this.elementValidations.length; i++) {
		try {
    		if(!this.elementValidations[i].validate()) isValid = false;
    	} catch(error) {
			isValid = false;
			alert(error);
		}
		if(this.stopAtInvalid && !isValid) break;
	}
	return isValid;
}


FormValidation.prototype.addElementValidation = function(elementValidation) {
	if(elementValidation instanceof ElementValidation) this.elementValidations[this.elementValidations.length] = elementValidation;
	return this;
}


function ElementValidation(element, validOnBlur, renderValid) {
	if(!element) return;
	this.element = element.nodeName ? element : document.getElementById(element);
	if(!this.element) return;
	
	this.validOnBlur = validOnBlur || false;
	this.renderValid = renderValid || false;
	
	var old_onfocus = this.element.onfocus || function() {};
	var old_onblur = this.element.onblur || function() {};
	var self = this;
	this.element.onfocus = function(e) { self.doOnFocus(); old_onfocus.call(this, e); }
    this.element.onblur = function(e) { self.doOnBlur(); old_onblur.call(this, e); }

	this.validations = new Array();
}

ElementValidation.prototype.validate = function() {
	try {    
    	for(var i=0; i<this.validations.length; i++) {
			if(this.validations[i].validate()) {
				this.valid(this.validations[i].getValidMsg());
			} else {
				this.invalid(this.validations[i].getInvalidMsg());
				return false;
			}
		}
		return true;
	} catch(error) {
		alert(error);
		return false;
	}
}

ElementValidation.prototype.doOnFocus = function() {}

ElementValidation.prototype.doOnBlur = function() {
	if(this.validOnBlur) this.validate();
}

ElementValidation.prototype.addValidation = function(validation) {
	if(validation instanceof Validation) {
		validation.setAttachedElement(this);
		this.validations[this.validations.length] = validation;
	}
	return this;
}

ElementValidation.prototype.invalid = function(msg) {
	this.reset();
	this.doInvalid(msg);
}

ElementValidation.prototype.valid = function(msg) {
	this.reset();
	if(this.renderValid) {
		this.doValid(msg);
	}
}

// should be implemented by subclass
ElementValidation.prototype.doInvalid = function(msg) {}
ElementValidation.prototype.doValid = function(msg) {}
ElementValidation.prototype.reset = function() {}

// Alert element validation
function AlertElementValidation(element, validOnBlur, renderValid, selectAtInvalid) {
	this.superClass(element, validOnBlur, renderValid);
	this.selectAtInvalid = selectAtInvalid || false;
}
AlertElementValidation.prototype = new ElementValidation();
AlertElementValidation.prototype.constructor = AlertElementValidation;
AlertElementValidation.prototype.superClass = ElementValidation;
AlertElementValidation.prototype.doInvalid = function(msg) {
	alert(msg);
	if(this.selectAtInvalid) this.element.select();
}
AlertElementValidation.prototype.doValid = function(msg) {
	alert(msg);
}


// Shadow element validation
ShadowElementValidation.valid_element_class = 'valid_element_class';
ShadowElementValidation.valid_message_class = 'valid_message_class';
ShadowElementValidation.invalid_element_class = 'invalid_element_class';
ShadowElementValidation.invalid_message_class = 'invalid_message_class';
ShadowElementValidation.focus_element_class = 'focus_element_class';

function ShadowElementValidation(element, validOnBlur, renderValid, toReside) {
	this.superClass(element, validOnBlur, renderValid);
	this.toReside = (toReside && toReside.nodeName) ? toReside : document.getElementById(toReside);

	this.oldClass = this.element.className;
	this.msgId = this.element.id + '_message';
}
ShadowElementValidation.prototype = new ElementValidation();
ShadowElementValidation.prototype.constructor = ShadowElementValidation;
ShadowElementValidation.prototype.superClass = ElementValidation;

ShadowElementValidation.prototype.doInvalid = function(msg) {
	this.set(ShadowElementValidation.invalid_element_class, msg, ShadowElementValidation.invalid_message_class);
}

ShadowElementValidation.prototype.doValid = function(msg) {
	this.set(ShadowElementValidation.valid_element_class, msg, ShadowElementValidation.valid_message_class);
}

ShadowElementValidation.prototype.reset = function() {
	this.removeClass();
	this.removeMessage();
}

ShadowElementValidation.prototype.set = function(elementClass, msg, msgClass) {
	this.addClass(elementClass);
	this.insertMessage(msg, msgClass);
}

ShadowElementValidation.prototype.doOnFocus = function() {
	this.reset();
	this.addClass(ShadowElementValidation.focus_element_class);
}

ShadowElementValidation.CreateMessage = function(id, msg, msgClass) {
        var span = document.createElement('span');
		span.className = msgClass;
		span.id = id;
    	var textNode = document.createTextNode(msg);
      	span.appendChild(textNode);
        return span;
}

ShadowElementValidation.prototype.insertMessage = function(msg, msgClass){
      	this.removeMessage();
		var msgElement = ShadowElementValidation.CreateMessage(this.msgId, msg, msgClass);
		if(!this.toReside) {
			if(this.element.nextSibling){
				this.element.parentNode.insertBefore(msgElement, this.element.nextSibling);
			}else{
				this.element.parentNode.appendChild(msgElement);
			}
		} else {
			this.toReside.appendChild(msgElement);
		}
}

ShadowElementValidation.prototype.removeMessage = function() {
	var next = (!this.toReside) ? this.element.nextSibling : this.toReside.firstChild;
	while(next) {
		if(next.id == this.msgId) {
			next.parentNode.removeChild(next);
			break;
		} else {
			next = next.nextSibling;
		}
	}
}

ShadowElementValidation.prototype.addClass = function(elementClass) {
        this.element.className = this.oldClass + ' ' + elementClass;
}

ShadowElementValidation.prototype.removeClass = function() {
        this.element.className = this.oldClass;
}


Validation.valid_message = '谢谢';
Validation.invalid_message = '请重新输入';
Validation.required_message = '不能为空';
Validation.format_message = '格式不正确';
Validation.email_message = 'Email格式不正确';
Validation.min_length_message = '必须大于';
Validation.max_length_message = '必须小于';
Validation.accept_message = '必须接受';
Validation.confirm_message = '必须等于';
Validation.custom_message = '必须是有效的输入';

//Required validation
function Validation(validMsg, invalidMsg) {
	this.validMsg = validMsg || Validation.valid_message;
	this.invalidMsg = invalidMsg;
	this.attachedElement = null;
}

Validation.prototype.validate = function() { return true; }

Validation.prototype.setAttachedElement = function(attachedElement) {
	this.attachedElement = attachedElement;
}

Validation.prototype.getValidMsg = function() { return this.validMsg; }
Validation.prototype.getInvalidMsg = function() { return this.invalidMsg; }


//Required validation
function RequiredValidation(validMsg, invalidMsg) {
	this.superClass(validMsg, invalidMsg || Validation.required_message);
}

RequiredValidation.prototype = new Validation();
RequiredValidation.prototype.constructor = RequiredValidation;
RequiredValidation.prototype.superClass = Validation;

RequiredValidation.prototype.validate = function() {
	if(!this.attachedElement.element.value) {
		return false;
	} else {
		return true;
	}
}


//Format validation
function FormatValidation(validMsg, invalidMsg, pattern, negate) {
	this.superClass(validMsg, invalidMsg || Validation.format_message);
	this.pattern = pattern || /./;
	this.negate = negate || false;
}

FormatValidation.prototype = new Validation();
FormatValidation.prototype.constructor = FormatValidation;
FormatValidation.prototype.superClass = Validation;

FormatValidation.prototype.validate = function() {
	var value = this.attachedElement.element.value;
	if(!this.negate) {
		if(this.pattern.test(value)) { return true; }
		else { return false; }
	} else {
	    if(this.pattern.test(value)) { return false; }
		else { return true; }
	}
}


//Email validation
function EmailValidation(validMsg, invalidMsg) {
	this.superClass2(validMsg, invalidMsg || Validation.email_message, /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i);
}

EmailValidation.prototype = new FormatValidation();
EmailValidation.prototype.constructor = EmailValidation;
EmailValidation.prototype.superClass2 = FormatValidation;


//Length validation
function LengthValidation(validMsg, minInvalidMsg, maxInvalidMsg, minLength, maxLength) {
	this.superClass(validMsg, Validation.invalid_message);
	this.minInvalidMsg = minInvalidMsg || Validation.min_length_message + ' ' + minLength;
	this.maxInvalidMsg = maxInvalidMsg || Validation.max_length_message + ' ' + maxLength;
	this.minLength = minLength || 0;
	this.maxLength = maxLength || Number.MAX_VALUE;
}

LengthValidation.prototype = new Validation();
LengthValidation.prototype.constructor = LengthValidation;
LengthValidation.prototype.superClass = Validation;

LengthValidation.prototype.validate = function() {
	var length = 0;
	if(this.attachedElement.element.value) length = this.attachedElement.element.value.length;

	if(length < this.minLength) {
		this.invalidMsg = this.minInvalidMsg;
		return false;
	} else if(length > this.maxLength) {
		this.invalidMsg = this.maxInvalidMsg;
		return false;
	} else {
		return true;
	}
}



//Accept validation
function AcceptValidation(validMsg, invalidMsg) {
	this.superClass(validMsg, invalidMsg || Validation.accept_message);
}

AcceptValidation.prototype = new Validation();
AcceptValidation.prototype.constructor = AcceptValidation;
AcceptValidation.prototype.superClass = Validation;

AcceptValidation.prototype.validate = function() {
	if(!this.attachedElement.element.checked) {
		return false;
	} else {
		return true;
	}
}


//Confirm validation
function ConfirmValidation(validMsg, invalidMsg, matchedElement) {
	this.superClass(validMsg, invalidMsg || Validation.confirm_message);
	this.matchedElement = (matchedElement && matchedElement.nodeName) ? matchedElement : document.getElementById(matchedElement);
}

ConfirmValidation.prototype = new Validation();
ConfirmValidation.prototype.constructor = ConfirmValidation;
ConfirmValidation.prototype.superClass = Validation;

ConfirmValidation.prototype.validate = function() {
	if(this.matchedElement && (this.attachedElement.element.value == this.matchedElement.value)) {
		return true;
	} else {
		return false;
	}
}


//Custom validation
function CustomValidation(validMsg, invalidMsg, customFunc) {
	this.superClass(validMsg, invalidMsg || Validation.custom_message);
	this.customFunc = customFunc || function() {};
}

CustomValidation.prototype = new Validation();
CustomValidation.prototype.constructor = CustomValidation;
CustomValidation.prototype.superClass = Validation;

CustomValidation.prototype.validate = function() {
	if(this.customFunc() !== false) {
		return true;
	} else {
		return false;
	}
}


Validation.REQUIRED = 'REQUIRED';
Validation.FORMAT = 'FORMAT';
Validation.LENGTH = 'LENGTH';
Validation.EMAIL = 'EMAIL';
Validation.ACCEPT = 'ACCEPT';
Validation.CONFIRM = 'CONFIRM';
Validation.CUSTOM = 'CUSTOM';

//Validation factory
function ValidationFactory() {}
ValidationFactory.prototype.create = function(validationType, paramsObj) {}


//N13Validation factory
function N13ValidationFactory() {}
N13ValidationFactory.prototype = new ValidationFactory();
N13ValidationFactory.prototype.constructor = N13ValidationFactory;
N13ValidationFactory.prototype.superClass = ValidationFactory;

N13ValidationFactory.prototype.create = function(validationType, paramsObj) {
	paramsObj = paramsObj || {};
	validationType = validationType || '';
	switch(validationType.toUpperCase()) {
		case Validation.REQUIRED : return new RequiredValidation(paramsObj.validMsg, paramsObj.invalidMsg);
		case Validation.FORMAT   : return new FormatValidation(paramsObj.validMsg, paramsObj.invalidMsg, paramsObj.pattern, paramsObj.negate);
		case Validation.LENGTH   : return new LengthValidation(paramsObj.validMsg, paramsObj.minInvalidMsg, paramsObj.maxInvalidMsg, paramsObj.minLength, paramsObj.maxLength);
		case Validation.EMAIL    : return new EmailValidation(paramsObj.validMsg, paramsObj.invalidMsg);
		case Validation.ACCEPT   : return new AcceptValidation(paramsObj.validMsg, paramsObj.invalidMsg);
		case Validation.CONFIRM  : return new ConfirmValidation(paramsObj.validMsg, paramsObj.invalidMsg, paramsObj.matchedElement);
		case Validation.CUSTOM   : return new CustomValidation(paramsObj.validMsg, paramsObj.invalidMsg, paramsObj.customFunc);
	}
	return null;
}

// N13 Form Validation
var N13FormValidation = {
	forms : new Array(),
	elements : new Array(),
	factory : new N13ValidationFactory(),
	registerForm : function(formId, paramsObj) {
		paramsObj = paramsObj || {};
		this.forms[formId] || (this.forms[formId] = new FormValidation( formId, paramsObj.stopAtInvalid, paramsObj.onSuccess));
	},
	registerElement : function(formId, elementId, paramsObj) {
		if(!this.elements[elementId]) {
			this.registerForm(formId);
			paramsObj = paramsObj || {};
			this.elements[elementId] = new ShadowElementValidation(elementId, paramsObj.validOnBlur, paramsObj.renderValid, paramsObj.toReside);
			this.forms[formId].addElementValidation(this.elements[elementId]);
		}
	},
	registerValidator : function(elementId, validatorType, paramsObj) {
		this.elements[elementId] && this.elements[elementId].addValidation( this.factory.create(validatorType, paramsObj) );
	},
	register : function(formId, elements) {
		if(!formId) return;
		elements = elements || [];
		this.registerForm(formId);
		for(var i=0; i<elements.length; i++) {
			var element = elements[i];
			if(element.id) {
				this.registerElement(formId, element.id, element);
				this.registerValidator(element.id, element.type, element);
			}
		}
	}
};

var N13 = N13FormValidation;